﻿using System;
using System.Collections.Generic;
using System.Text;

using log4net;
using log4net.Config;

namespace HouseOfCards
{
   internal static class HandEvaluator
   {
      private static readonly ILog _logger = LogManager.GetLogger("videopoker");

      private static int _coinsPlayed;
      private static GameTypes _game;

      private static PayOut BasicEvaluation(Hand hand)
      {
         PayOut po = new PayOut(hand.CoinValue);
         po.Credits = 0;
         po.PokerHand = PokerHands.HighCard;

         if (hand.CardCount < 5) return po;

         int[] pips = new int[13];
         int[] suits = new int[4];
         int pip = 0;
         int suit = 0;
         int idx = 0;
         bool flush = false;
         bool straight = false;
         int pairCards = 0;
         int pairCount = 0;

         _logger.DebugFormat("===================================");
         suit = (int)hand.Card1.Suit;
         pip = (int)hand.Card1.Face;
         pips[pip] += 1;
         suits[suit] += 1;
         _logger.DebugFormat(String.Format("Card 1 pip=:{0} suit:={1}", pip, suit));

         suit = (int)hand.Card2.Suit;
         pip = (int)hand.Card2.Face;
         pips[pip] += 1;
         suits[suit] += 1;
         _logger.DebugFormat(String.Format("Card 2 pip=:{0} suit:={1}", pip, suit));

         suit = (int)hand.Card3.Suit;
         pip = (int)hand.Card3.Face;
         pips[pip] += 1;
         suits[suit] += 1;
         _logger.DebugFormat(String.Format("Card 3 pip=:{0} suit:={1}", pip, suit));

         suit = (int)hand.Card4.Suit;
         pip = (int)hand.Card4.Face;
         pips[pip] += 1;
         suits[suit] += 1;
         _logger.DebugFormat(String.Format("Card 4 pip=:{0} suit:={1}", pip, suit));

         suit = (int)hand.Card5.Suit;
         pip = (int)hand.Card5.Face;
         pips[pip] += 1;
         suits[suit] += 1;
         _logger.DebugFormat(String.Format("Card 5 pip=:{0} suit:={1}", pip, suit));

         //check for a flush
         if (suits[suit] == 5)
         {
            flush = true;
         }

         // Count pair members
         for (idx = 0; idx <= 12; idx++)
         {
            if (pips[idx] > 1)
            {
               pairCount += 1; // Number of different pairs
               pairCards += pips[idx]; // Total number of cards used making pairs
            }
         }

         _logger.DebugFormat(String.Format("pairCount=:{0} pairCards:={1}", pairCount, pairCards));

         // Find Straights
         pip = 0;
         for (idx = 0; idx <= 12; idx++)
         {
            if (pips[idx] == 1)
            {
               pip += 1;
               if (pip == 5)
               {
                  straight = true;
               }
            }
            else
            {
               pip = 0;
            }
         }

         // Catch 10 - A straight
         if ((pip == 4) && (pips[0] == 1))
         {
            straight = true;
         }


         // I now have enough to check for winning hands
         // Starting from the best hand on down, I will simply
         // return if I find a winner

         // Royal Flush / Straight Flush
         if (straight && flush)
         {
            // Got a straight flush, but is it royal?
            pip = pips[0]; // Ace
            for (idx = 9; idx <= 12; idx++) // 10-King
            {
               pip += pips[idx];
            }

            if (pip == 5)
            {
               po.PokerHand = PokerHands.RoyalFlush;
               po.Credits = CalcPayout(po.PokerHand);
               return po;
            }
            else
            {
               po.PokerHand = PokerHands.StraightFlush;
               po.Credits = CalcPayout(po.PokerHand);
               return po;
            }
         }

         // 4 of a kind
         if (pairCards >= 4 && pairCount == 1)
         {
            po.PokerHand = PokerHands.FourOfAKind;

            if (_game == GameTypes.DoubleBonus)
            {
               if (pips[0] == 4)
               {
                  //four aces
                  po.PokerHand = PokerHands.FourAces;
               }
               else if (pips[1] == 4 || pips[2] == 4 || pips[3] == 4)
               {
                  //four aces
                  po.PokerHand = PokerHands.FourTwoThreeOrFour;
               }
               else
               {
                  po.PokerHand = PokerHands.FourFiveThruKing;
               }
            }

            po.Credits = CalcPayout(po.PokerHand);
            return po;
         }

         // Full House
         if (pairCards == 5 && pairCount == 2)
         {
            //return (int)Win.FullHouse;
            po.PokerHand = PokerHands.FullHouse;
            po.Credits = CalcPayout(po.PokerHand);
            return po;
         }

         // Flush / Straight
         if (flush)
         {
            //return (int)Win.Flush;
            po.PokerHand = PokerHands.Flush;
            po.Credits = CalcPayout(po.PokerHand);
            return po;
         }
         if (straight)
         {
            //return (int)Win.Straight;
            po.PokerHand = PokerHands.Straight;
            po.Credits = CalcPayout(po.PokerHand);
            return po;
         }

         // 3 of a Kind
         if (pairCards == 3)
         {
            //return (int)Win.ThreeKind;
            po.PokerHand = PokerHands.ThreeOfAKind;
            po.Credits = CalcPayout(po.PokerHand);
            return po;
         }

         // 2 Pair
         if (pairCount == 2)
         {
            //return (int)Win.TwoPair;
            po.PokerHand = PokerHands.TwoPair;
            po.Credits = CalcPayout(po.PokerHand);
            return po;
         }

         // Pair
         if (pairCards == 2)
         {
            // Got a pair, is it Jacks or better?
            if (pips[0] > 1) // Aces
            {
               po.PokerHand = PokerHands.JacksOrBetter;
               po.Credits = CalcPayout(po.PokerHand);
               return po;
            }
            for (idx = 10; idx <= 12; idx++) // Jack to King
            {
               if (pips[idx] > 1)
               {
                  po.PokerHand = PokerHands.JacksOrBetter;
                  po.Credits = CalcPayout(po.PokerHand);
                  return po;
               }
            }
         }

         //nothing
         po.Credits = 0;
         po.PokerHand = PokerHands.HighCard;

         return po;
      }

      /// <summary>
      /// Calculate the pay out for the hand
      /// </summary>
      /// <param name="pokerHand">the hand</param>
      private static int CalcPayout(PokerHands pokerHand)
      {
         int pay = 0;

         switch (pokerHand)
         {
            case PokerHands.RoyalFlush:
               if (_coinsPlayed == 5)
               {
                  pay = 4000;
               }
               else
               {
                  pay = _coinsPlayed * 250;
               }
               break;
            case PokerHands.StraightFlush:
               pay = _coinsPlayed * 50;
               break;
            case PokerHands.FourAces:
               pay = _coinsPlayed * 160;
               break;
            case PokerHands.FourTwoThreeOrFour:
               pay = _coinsPlayed * 80;
               break;
            case PokerHands.FourFiveThruKing:
               pay = _coinsPlayed * 50;
               break;
            case PokerHands.FourOfAKind:
               pay = _coinsPlayed * 25;
               break;
            case PokerHands.FullHouse:
               if (_game == GameTypes.DoubleBonus)
               {
                  pay = _coinsPlayed * 10;
               }
               else
               {
                  pay = _coinsPlayed * 9;
               }
               break;
            case PokerHands.Flush:
               if (_game == GameTypes.DoubleBonus)
               {
                  pay = _coinsPlayed * 7;
               }
               else
               {
                  pay = _coinsPlayed * 6;
               }
               break;
            case PokerHands.Straight:
               if (_game == GameTypes.DoubleBonus)
               {
                  pay = _coinsPlayed * 5;
               }
               else
               {
                  pay = _coinsPlayed * 4;
               }
               break;
            case PokerHands.ThreeOfAKind:
               pay = _coinsPlayed * 3;
               break;
            case PokerHands.TwoPair:
               if (_game == GameTypes.DoubleBonus)
               {
                  pay = _coinsPlayed * 1;
               }
               else
               {
                  pay = _coinsPlayed * 2;
               }
               break;
            case PokerHands.JacksOrBetter:
               pay = _coinsPlayed * 1;
               break;
         }
         return pay;
      }

      public static PayOut Evaluate(Hand hand)
      {
         _coinsPlayed = hand.CoinsPlayed;
         _game = hand.GameType;

         PayOut payOut = BasicEvaluation(hand);

         return payOut;
      }
   }
}
